package cz.proks.easyhelpdesk.gae.core.util;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import com.google.api.client.auth.oauth2.draft10.AccessTokenResponse;
import com.google.api.client.googleapis.auth.oauth2.draft10.GoogleAccessTokenRequest.GoogleAuthorizationCodeGrant;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson.JacksonFactory;

import cz.proks.easyhelpdesk.api.config.Configuration;
import cz.proks.easyhelpdesk.api.exception.ApiException;
import cz.proks.easyhelpdesk.api.model.OAuth2Token;

public class OAuth2Verifier {
    enum VerifierProvider {
        UNKNOWN(null), GOOGLE("https://accounts.google.com/o/oauth2/token");

        private final String providerUrl;

        private VerifierProvider(String providerUrl) {
            this.providerUrl = providerUrl;
        }

        public String getProviderUrl() {
            return providerUrl;
        }

        public static VerifierProvider forUrl(String providerUrl) {
            if (!StringUtils.hasText(providerUrl))
                return UNKNOWN;

            try {
                String url = URLDecoder.decode(providerUrl, "UTF-8");

                if (GOOGLE.getProviderUrl().equals(url))
                    return GOOGLE;
                else
                    return UNKNOWN;
            } catch (UnsupportedEncodingException e) {
                return UNKNOWN;
            }
        }
    }

    private final static Logger log = LoggerFactory.getLogger(OAuth2Verifier.class);

    public static OAuth2Token verify(String endpointUrl, String authorizationToken) throws ApiException {
        VerifierProvider provider = VerifierProvider.forUrl(endpointUrl);
        switch (provider) {
            case GOOGLE:
                return verifyGoogle(endpointUrl, authorizationToken);

            case UNKNOWN:
            default:
                throw new IllegalArgumentException("Unknown oauth2 endpoint '" + endpointUrl + "'");
        }

    }

    private static OAuth2Token verifyGoogle(String endpoint, String authorizationToken) throws ApiException {
        log.debug("verifyGoogle():: Start with endpoint: '{}', authorizationToken: '{}'", endpoint, authorizationToken);
        
        HttpTransport transport = new NetHttpTransport();        
        JsonFactory jsonFactory = new JacksonFactory();        
        
        OAuth2Token token = null;
        
        try {     
            
            GoogleAuthorizationCodeGrant authRequest = new GoogleAuthorizationCodeGrant(transport, jsonFactory, Configuration.GWT.GOOGLE_OAUTH.CLIENT_ID, Configuration.GWT.GOOGLE_OAUTH.CLIENT_SECRET, authorizationToken, Configuration.GWT.OAUTH2_REDIRECT_URI);
         
            log.debug("verifyGoogle():: Authorizing with clientId: '{}', clientSecret: '{}', authorizationToken: '{}', redirectUri: '{}'", new Object[] {Configuration.GWT.GOOGLE_OAUTH.CLIENT_ID, Configuration.GWT.GOOGLE_OAUTH.CLIENT_SECRET, authorizationToken, Configuration.GWT.OAUTH2_REDIRECT_URI});
            
            AccessTokenResponse authResponse = authRequest.execute();
            
            token = getFrom(authResponse);
            
            log.debug("verifyGoogle():: Authorized with token: '{}'", token);
            return token;
        } catch (IOException e) {
            throw new ApiException(e);
        } finally {
            log.debug("verifyGoogle():: Ends with token: '{}'", token);
        }
    }

    private static OAuth2Token getFrom(AccessTokenResponse authResponse) {
        if (authResponse == null)
            return null;

        OAuth2Token oauthToken = new OAuth2Token();

        oauthToken.setAccessToken(authResponse.accessToken);
        oauthToken.setRefreshToken(authResponse.refreshToken);
        //oauthToken.setExpirationDate(new Date(System.currentTimeMillis() + authResponse.expiresIn * 1000));

        if (StringUtils.hasText(authResponse.scope))
            //oauthToken.setScopes(authResponse.scope.split(" "));

        if (authResponse.containsKey("id_token"))
            oauthToken.setIdToken(authResponse.get("id_token").toString());
        
        if (authResponse.containsKey("token_type"))
            oauthToken.setTokenType(authResponse.get("token_type").toString());

        return oauthToken;
    }
}
